package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.UserUtil;
import org.oslims.client.service.AnalysisResultServiceAsync;
import org.oslims.client.service.AnalysisServiceAsync;
import org.oslims.client.service.RequestServiceAsync;
import org.oslims.client.service.SampleServiceAsync;
import org.oslims.client.uicomponents.SelectionColumn;
import org.oslims.client.uicomponents.TagTreeItem;
import org.oslims.client.uicomponents.WidgetUtils;
import org.oslims.client.view.AnalysisPlaningView;
import org.oslims.client.view.impl.AnalysisPlaningViewImpl;
import org.oslims.domain.AbstractSample;
import org.oslims.domain.Analysis;
import org.oslims.domain.AnalysisResult;
import org.oslims.domain.LabStaff;
import org.oslims.domain.Request;
import org.oslims.domain.SampleType;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.ListView.Delegate;
import com.google.gwt.view.client.SelectionModel.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionModel.SelectionChangeHandler;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = AnalysisPlaningViewImpl.class)
public class AnalysisPlaningPresenter extends BasePresenter<AnalysisPlaningView, MainEventBus> {

	private RequestServiceAsync requestService;
	private SampleServiceAsync sampleService;
	private AnalysisServiceAsync analysisService;
	private AnalysisResultServiceAsync analysisResultService;

	private MultiSelectionModel<AbstractSample> sampleSelection = new MultiSelectionModel<AbstractSample>();
	private MultiSelectionModel<Analysis> analysisSelection = new MultiSelectionModel<Analysis>();

	private MultiSelectionModel<AnalysisResult> analysisResultSelection = new MultiSelectionModel<AnalysisResult>();

	public AnalysisPlaningPresenter() {
	}

	public void onShowAnalysisPlaning(DockLayoutPanel mainWidget) {

		WidgetUtils.removeCenterWidget(mainWidget);
		mainWidget.add(view.getViewWidget());

		view.getSamplesTree().clear();

		// Clear sample selection
		List<AbstractSample> selectedSamples = new ArrayList<AbstractSample>();

		for (AbstractSample s : sampleSelection.getSelectedSet()) {
			selectedSamples.add(s);
		}

		for (AbstractSample s : selectedSamples) {
			sampleSelection.setSelected(s, false);
		}

		// Clear analysis selection
		List<Analysis> selectedAnalysis = new ArrayList<Analysis>();

		for (Analysis a : analysisSelection.getSelectedSet()) {
			selectedAnalysis.add(a);
		}

		for (Analysis a : selectedAnalysis) {
			analysisSelection.setSelected(a, false);
		}

		view.getAnalysisList().refresh();
		refreshRequests();
	}

	private void refreshRequests() {
		requestService.getAllRequest(new AsyncCall<List<Request>>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(List<Request> result) {
				for (Request r : result) {
					TagTreeItem ti = new TagTreeItem();
					ti.setTag(r);
					ti.setText(r.getCode());
					ti.addItem("...");
					view.getSamplesTree().addItem(ti);
				}
			}
		});
	}

	@Override
	// Set up the required click handlers
	public void bind() {

		bindAnalysisList();

		setUpTreeHandler();

		setUpSampleSelectionHandler();

		bindAddAnalysisButton();

		bindDeadlineBox();

		bindPlaningBox();

	}

	private void bindPlaningBox() {
		view.getPlaningBox().getOKButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getPlaningBox().hide();
			}
		});

		view.getPlaningBox().getTable().setSelectionModel(analysisResultSelection);
		view.getPlaningBox().getTable().addColumn(new SelectionColumn<AnalysisResult>(analysisResultSelection));

		view.getPlaningBox().getTable().addColumn(new TextColumn<AnalysisResult>() {

			@Override
			public String getValue(AnalysisResult object) {
				return object.getAnalysis().getDoneIn().getName();
			}
		}, "Dept.");

		view.getPlaningBox().getTable().addColumn(new TextColumn<AnalysisResult>() {

			@Override
			public String getValue(AnalysisResult object) {
				return object.getAnalysis().getName();
			}
		}, "Analysis");

		view.getPlaningBox().getTable().addColumn(new TextColumn<AnalysisResult>() {

			@Override
			public String getValue(AnalysisResult object) {
				return object.getState();
			}
		}, "State");

		view.getPlaningBox().getTable().addColumn(new TextColumn<AnalysisResult>() {

			@SuppressWarnings("deprecation")
			@Override
			public String getValue(AnalysisResult object) {
				return object.getDeadline().toLocaleString();
			}
		}, "Deadline");

		view.getPlaningBox().getTable().addColumn(new TextColumn<AnalysisResult>() {

			@Override
			public String getValue(AnalysisResult object) {
				return object.getAssignedBy().toString();
			}
		}, "Assigned By");
	}

	private void bindDeadlineBox() {
		view.getSelectDeadlineBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				createPlaningForSelection();
			}
		});

		view.getSelectDeadlineBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getSelectDeadlineBox().hide();
			}
		});
	}

	private void bindAddAnalysisButton() {
		view.getPlanAnalysisButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getSelectDeadlineBox().show();
			}
		});
	}

	private void setUpSampleSelectionHandler() {
		sampleSelection.addSelectionChangeHandler(new SelectionChangeHandler() {

			@Override
			public void onSelectionChange(SelectionChangeEvent event) {
				view.getAnalysisList().refresh();
			}
		});
	}

	private void setUpTreeHandler() {
		view.getSamplesTree().addOpenHandler(new OpenHandler<TreeItem>() {

			@Override
			public void onOpen(OpenEvent<TreeItem> arg0) {
				final TagTreeItem ti = (TagTreeItem) arg0.getTarget();
				ti.removeItems();

				final CellTable<AbstractSample> sampleTable = new CellTable<AbstractSample>();
				sampleTable.setSelectionModel(sampleSelection);

				sampleTable.addColumn(new SelectionColumn<AbstractSample>(sampleSelection));

				sampleTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getCode();
					}
				}, "Code");

				sampleTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getSampleType().toString();
					}
				}, "Sample Type");

				sampleTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getOrigin();
					}
				}, "Origin");

				sampleTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getStoragePlace();
					}
				}, "Storage Place");

				sampleTable.addColumn(new TextColumn<AbstractSample>() {

					@Override
					public String getValue(AbstractSample object) {
						return object.getState();
					}
				}, "State");

				Column<AbstractSample, String> showPlaningCol = new Column<AbstractSample, String>(new ButtonCell()) {

					@Override
					public String getValue(AbstractSample object) {
						return "Planing";
					}
				};

				showPlaningCol.setFieldUpdater(new FieldUpdater<AbstractSample, String>() {

					@Override
					public void update(int index, AbstractSample object, String value) {
						showPlaningForSample(object);
					}

				});

				sampleTable.addColumn(showPlaningCol);

				sampleTable.setDelegate(new Delegate<AbstractSample>() {

					@Override
					public void onRangeChanged(ListView<AbstractSample> listView) {
						sampleService.getAllSamplesReadyForAnalysisByRequest((Request) ti.getTag(), new AsyncCall<List<AbstractSample>>() {

							@Override
							protected void onFailureE(Throwable caught) {
								eventBus.error(caught.getMessage());
							}

							@Override
							protected void onSuccessE(List<AbstractSample> result) {
								sampleTable.setPageSize(result.size());
								sampleTable.setData(0, result.size(), result);
								sampleTable.redraw();
							}
						});
					}
				});

				ti.addItem(sampleTable);
				sampleTable.refresh();
			}
		});
	}

	private void bindAnalysisList() {
		view.getAnalysisList().addColumn(new SelectionColumn<Analysis>(analysisSelection));

		view.getAnalysisList().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				return object.getDoneIn().getAbbreviation();
			}
		}, "Dept.");

		view.getAnalysisList().addColumn(new TextColumn<Analysis>() {

			@Override
			public String getValue(Analysis object) {
				return object.getName();
			}
		}, "Name");

		view.getAnalysisList().setSelectionModel(analysisSelection);

		view.getAnalysisList().setDelegate(new Delegate<Analysis>() {

			@Override
			public void onRangeChanged(ListView<Analysis> listView) {

				List<SampleType> st = new ArrayList<SampleType>();

				for (AbstractSample s : sampleSelection.getSelectedSet()) {
					st.add(s.getSampleType());
				}

				analysisService.getAnalysisForSampleTypes(st, new AsyncCall<List<Analysis>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Analysis> result) {
						view.getAnalysisList().setPageSize(result.size());
						view.getAnalysisList().setData(0, result.size(), result);
						view.getAnalysisList().redraw();

						view.getAnalysisList().setVisible(result.size() != 0);
						view.getNoAnalysisAvailableLabel().setVisible(result.size() == 0);

					}
				});
			}
		});
	}

	private void showPlaningForSample(AbstractSample object) {

		analysisResultService.getAnalysisResultsBySample(object, new AsyncCall<List<AnalysisResult>>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(List<AnalysisResult> result) {
				if (result.size() == 0) {
					eventBus.error("Sample is not yet planed!");
					return;
				}

				view.getPlaningBox().getTable().setPageSize(result.size());
				view.getPlaningBox().getTable().setData(0, result.size(), result);
				view.getPlaningBox().getTable().redraw();
				view.getPlaningBox().show(result.get(0).getSample());
			}
		});
	}

	private void createPlaningForSelection() {
		List<AnalysisResult> newAnalysis = new ArrayList<AnalysisResult>();

		for (AbstractSample s : sampleSelection.getSelectedSet()) {
			for (Analysis a : analysisSelection.getSelectedSet()) {
				AnalysisResult ar = new AnalysisResult();
				ar.setAnalysis(a);
				ar.setSample(s);
				ar.setAssignedAt(new Date());
				ar.setDeadline(view.getSelectDeadlineBox().getDeadline().getInputField().getValue());
				ar.setState("In Analyzing");
				ar.setAssignedBy((LabStaff) UserUtil.getLoggedInUser());
				newAnalysis.add(ar);
			}
		}

		analysisResultService.saveNewAnalysisResults(newAnalysis, new AsyncCall<Void>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@SuppressWarnings("unchecked")
			@Override
			protected void onSuccessE(Void result) {
				view.getSelectDeadlineBox().hide();

				for (int i = 0; i < view.getSamplesTree().getItemCount(); i++) {
					TreeItem ti = view.getSamplesTree().getItem(i);

					if (ti.getState()) {
						CellTable<AbstractSample> table = (CellTable<AbstractSample>) ti.getChild(0).getWidget();
						table.refresh();
					}
				}
			}
		});
	}

	@InjectService
	public void setRequestService(RequestServiceAsync requestService) {
		this.requestService = requestService;
	}

	@InjectService
	public void setSampleService(SampleServiceAsync sampleService) {
		this.sampleService = sampleService;
	}

	@InjectService
	public void setAnalysisService(AnalysisServiceAsync analysisService) {
		this.analysisService = analysisService;
	}

	@InjectService
	public void setAnalysisResultService(AnalysisResultServiceAsync analysisResultService) {
		this.analysisResultService = analysisResultService;
	}

}